package lsp

var intSchemaMessage = map[string]string{
	"required": "{{#label}} is not allowed to be empty",
	"equal":    "{{#label}} must be {{#target}}",
	"not":      "{{#label}} cannot be {{#target}}",
	"valid":    "{{#label}} must be one of {{#target}}",
	"invalid":  "{{#label}} cannot be one of {{#target}}",
	"greater":  "{{#label}} must be greater than {{#limit}}",
	"less":     "{{#label}} must be less than {{#limit}}",
	"max":      "{{#label}} must be less than or equal to {{#limit}}",
	"min":      "{{#label}} must be greater than or equal to {{#limit}}",
	"multiple": "{{#label}} must be a multiple of {{#multiple}}",
	"negative": "{{#label}} must be a negative number",
	"positive": "{{#label}} must be a positive number",
	"port":     "{{#label}} must be a valid port",
}

func NewInt() *intSchema {
	return &intSchema{
		rules:     map[string]func(value int64) error{},
		whitelist: make([]int64, 0),
		blacklist: make([]int64, 0),
		anySchema: anySchema{
			label:   "value",
			message: intSchemaMessage,
		},
	}
}

type intSchema struct {
	anySchema
	whitelist []int64
	blacklist []int64
	rules     map[string]func(value int64) error
}

func (schema *intSchema) addRule(method string, hook func(value int64) error) *intSchema {
	schema.rules[method] = hook
	return schema
}

// Validate a value using the schema.
func (schema *intSchema) Validate(value int64) error {
	for _, hook := range schema.rules {
		if err := hook(value); nil != err {
			return err
		}
	}
	return nil
}

// Custom adds a custom validation function.
func (schema *intSchema) Custom(fn func(value int64) error) *intSchema {
	return schema.addRule("custom", fn)
}

// Label overrides the key name in error messages.
func (schema *intSchema) Label(name string) *intSchema {
	schema.label = name
	return schema
}

// Required requires the number value is not allowed to be 0.
func (schema *intSchema) Required() *intSchema {
	return schema.addRule("required", func(value int64) error {
		if 0 == value {
			return schema.error("required", nil)
		}
		return nil
	})
}

// Equal requires the number value for target.
// target - allowed target values.
func (schema *intSchema) Equal(target int64) *intSchema {
	return schema.addRule("equal", func(value int64) error {
		if value == target {
			return nil
		}
		return schema.error("equal", map[string]interface{}{"target": target})
	})
}

// Not required value cannot be the target value.
// target - disallow target values.
func (schema *intSchema) Not(target int64) *intSchema {
	return schema.addRule("not", func(value int64) error {
		if value == target {
			return schema.error("not", map[string]interface{}{"target": target})
		}
		return nil
	})
}

// Allow whitelists a value
func (schema *intSchema) Allow(values ...int64) *intSchema {
	schema.whitelist = append(schema.whitelist, values...)
	return schema
}

// Valid adds the provided values into the allowed whitelist and marks them as the only valid values allowed.
func (schema *intSchema) Valid(values ...int64) *intSchema {
	schema.Allow(values...)
	return schema.addRule("valid", func(value int64) error {
		for _, val := range schema.whitelist {
			if val == value {
				return nil
			}
		}
		return schema.error("valid", map[string]interface{}{"target": schema.whitelist})
	})
}

// Disallow blacklists a value
func (schema *intSchema) Disallow(values ...int64) *intSchema {
	schema.blacklist = append(schema.blacklist, values...)
	return schema
}

// Invalid adds the provided values into the allowed blacklist and marks them as the only invalid values disallow.
func (schema *intSchema) Invalid(values ...int64) *intSchema {
	schema.Disallow(values...)
	return schema.addRule("invalid", func(value int64) error {
		for _, val := range schema.blacklist {
			if val == value {
				return schema.error("invalid", map[string]interface{}{"target": schema.blacklist})
			}
		}
		return nil
	})
}

// Greater specifies that the value must be greater than limit.
func (schema *intSchema) Greater(limit int64) *intSchema {
	return schema.addRule("greater", func(value int64) error {
		if value > limit {
			return nil
		}
		return schema.error("greater", map[string]interface{}{"limit": limit})
	})
}

// Less specifies that the value must be less than limit.
func (schema *intSchema) Less(limit int64) *intSchema {
	return schema.addRule("less", func(value int64) error {
		if value < limit {
			return nil
		}
		return schema.error("less", map[string]interface{}{"limit": limit})
	})
}

// Max specifies the maximum value.
func (schema *intSchema) Max(limit int64) *intSchema {
	return schema.addRule("max", func(value int64) error {
		if value <= limit {
			return nil
		}
		return schema.error("max", map[string]interface{}{"limit": limit})
	})
}

// Min specifies the minimum value.
func (schema *intSchema) Min(limit int64) *intSchema {
	return schema.addRule("min", func(value int64) error {
		if value >= limit {
			return nil
		}
		return schema.error("min", map[string]interface{}{"limit": limit})
	})
}

// Multiple specifies that the value must be a multiple of base.
func (schema *intSchema) Multiple(base int64) *intSchema {
	return schema.addRule("multiple", func(value int64) error {
		if 0 == value%base {
			return nil
		}
		return schema.error("multiple", map[string]interface{}{"multiple": base})
	})
}

// Negative requires the number to be negative.
func (schema *intSchema) Negative() *intSchema {
	return schema.addRule("negative", func(value int64) error {
		if 0 > value {
			return nil
		}
		return schema.error("negative", nil)
	})
}

// Positive requires the number to be positive.
func (schema *intSchema) Positive() *intSchema {
	return schema.addRule("positive", func(value int64) error {
		if 0 < value {
			return nil
		}
		return schema.error("positive", nil)
	})
}

// Port requires the number to be a TCP port, so between 0 and 65535.
func (schema *intSchema) Port() *intSchema {
	return schema.addRule("port", func(value int64) error {
		if value >= 0 && value <= 65535 {
			return nil
		}
		return schema.error("port", nil)
	})
}
